//== SAGITTARIUS ============================================================================

//== FILEDOC =========================================================================

/** @file builtins.h
  * @brief builtins
  */

//== BEGINNING OF CODE ===============================================================

#ifndef __SAGUTTARUIS__BUILTINS__H_
#define __SAGUTTARUIS__BUILTINS__H_

//== INCLUDES ========================================================================

#include "sagittarius/core/SagittariusCore.h"
#include "sagittarius/symtab/symtab.h"
#include "sagittarius/types/types.h"
#include "sagittarius/types/exttypes1.h"
#include "sagittarius/io/io.h"

#ifdef __cplusplus
    #include <string>
#endif

#ifdef __cplusplus
extern "C" {
#endif

#define SG_TYPE_CHECK(obj, tp_name) (obj == (sg_type*)sg_global_session()->bt->tp_name)

typedef struct __builtin_type_ctx {
    /// none - uninitialized (different from nil)
    sg_type* none;
    /// ref
    sg_type* ref;
    /// type
    sg_type_tp* tp;
    /// emp (empty)
    sg_type* emp;
    /// int (integer)
    sg_int_tp* it;
    /// real
    sg_real_tp* real;
    /// complex
    sg_complex_tp* complex;
    /// str (string)
    sg_str_tp* str;
    /// nil
    sg_type* nil;
    /// Function
    sg_fun_tp* fun;
    /// grav lens
    sg_mir_tp* mir;
    /// name-value
    sg_name_tp* name;
    /// symbol instance
    sg_symbol_inst_tp* syminst;
    /// ctx
    sg_fctx_tp* fctx;
    /// fieldrec
    sg_fieldrec_tp* fieldrec;
    /// fieldtab
    sg_fieldtab_tp* fieldtab;
    /// type_dtran
    sg_type_dtran_tp* type_dtran;
    /// type_dtable
    sg_type_dtable_tp* type_dtable;
    /// module
    sg_mod_type* mod;
    /// module loader
    sg_mod_type* modld;
    /// list
    sg_list_tp* list;
    /// list
    sg_list_iter_tp* list_iter;
    /// locals
    sg_locals_tp* locals;
    /// node
    sg_locals_tp* node;
    /// reaction rule
    sg_locals_tp* rxn_rule;
    /// network
    sg_locals_tp* network;
    
    /// empty hidden class
    sg_hc* hc_base;
    /// standard prototype
    sg_hc* hc_obj;
} builtin_type_ctx;

// -- D table --

sg_type_dtable_val* make_empty_dtable();

sg_type_dtable_val* copy_dtable(sg_type_dtable_val* src);

sg_type_dtable_val* copy_center_dtable(sg_type_dtable_val* src, sg_hc* hc);

sg_type_dtran_val* copy_dtran_value(sg_type_dtran_val* src);

sg_type_dtran_val* dtable_new_dtran(sg_type_dtable_val* dt, symcode s, sg_hc* dst);

sg_value* sg_get_fieldr(sg_value* v, uint64 o);

sg_value** sg_get_fieldl(sg_value* v, uint64 o);

sg_type_dtran_val* sg_hc_get_tran(sg_hc* src, symcode s);

sg_type_dtran_val* sg_hc_tran_auto(sg_hc* src, symcode s);

sg_type_dtran_val* dtable_push_tran(sg_type_dtable_val* dt, sg_type_dtran_val* t);

void release_dtable(sg_type_dtable_val* d);

// -- Fields --

sg_fieldrec_val* sg_push_field(sg_fieldtab_val* ft, sg_fieldrec_val* r);

/// Add a static member function to the type
sg_fieldrec_val* sg_make_static_fun_field(sg_fun_val* fn);

/// Get the field associated with symbol s; return NULL if not found
sg_fieldrec_val* sg_lookup_field(sg_fieldtab_val* ft, symcode s);

void sg_init_val(sg_value* v);

// -- HC --

void sg_init_hc(sg_hc* hc, builtin_type_ctx* bt);

// -- Types --

void init_sg_tp(sg_type* t, builtin_type_ctx* bt);

void sg_copy_hc(sg_hc* dst, sg_hc* src);

builtin_type_ctx* create_builtin_type_ctx();

void install_builtin_syms(symtab* st, builtin_type_ctx* bc);

void release_builtin_type_ctx(builtin_type_ctx* bc);

void dump_type_repr(int fd, sg_type* t, symtab* st);

void sg_init_val(sg_value* v);

void sg_val_incref(sg_value* v);

void sg_val_decref(sg_value* v);

sg_value* sg_create_int(int64 x);

sg_value* sg_create_uint(uint64 x);

uint64 sg_check_int(sg_int_val* i);

sg_int_val* sg_cast_int(sg_value* v);

sg_real_val* sg_cast_to_real(sg_value* v);

// -- str --

sg_value* sg_create_str(void* _b, uint64 n);

uint64 sg_check_str(sg_str_val* str);

sg_str_val* sg_cast_str(sg_value* v);

// -- fun --

sg_fun_val* sg_create_fun();

void sg_release_fun(sg_fun_val* f);

uint64 sg_check_fun_val(sg_fun_val* f);

sg_fun_val* sg_cast_fun_val(sg_value* v);

void sg_rt_call_check(sg_fun_val* f, uint64 nargs);

typedef struct {
    sg_value* obj;
    sg_value* nil;
} sg_standard_protos;

// sg_standard_protos* create_builtin_protos(builtin_type_ctx* bt, builtin_fun_ctx* bf);

void sg_instantiate_proto(sg_value* dst, sg_value* src);

// ** list **

sg_list_val* sg_create_list();

void sg_release_list(sg_list_val* list);

void sg_list_append(sg_list_val* list, sg_value* obj);

void sg_list_reserve(sg_list_val* list, uint64 n);

sg_value* sg_list_get_by_index(sg_list_val* list, uint64 i);

void sg_list_store_by_index(sg_list_val* list, sg_value* obj, uint64 i);

// ** list iterators **

void sg_release_list_iter(sg_list_iter_val* iter);

sg_list_iter_val* sg_list_get_iter(sg_list_val* list);

void sg_list_iter_next(sg_list_iter_val* iter);

sg_value* sg_list_iter_deref(sg_list_iter_val* iter);

// ** locals **

sg_locals_val* sg_create_locals(sg_locals_val* parent);

/// Get parent locals structure
sg_locals_val* sg_locals_get_parent(sg_locals_val* locals);

/// Get local by index
sg_value* sg_locals_get_obj_by_index(sg_locals_val* locals, uint64 i);
sg_symbol_inst* sg_locals_get_sym_by_index(sg_locals_val* locals, uint64 i);

/// Store local by index
void sg_locals_store_by_index(sg_locals_val* locals, sg_value* obj, uint64 i);

void sg_release_locals(sg_locals_val* locals);

// ** symbol instance **

sg_symbol_inst* sg_create_symbol_inst();

/// Init with symbol
sg_symbol_inst* sg_create_symbol_inst_s(symcode s);

/// Cast to symbol instance
sg_symbol_inst* sg_cast_as_symbol_inst(sg_value* obj);

void sg_bind_obj_to_symbol(sg_symbol_inst* syminst, sg_value* obj);

void sg_release_symbol_inst(sg_symbol_inst* syminst);

// ** name-value **

sg_name_val* sg_cast_as_name_val(sg_value* obj);

// ** network **

sg_network_val* sg_create_network();

sg_network_val* sg_cast_to_network();

char* sg_network_repr(sg_network_val* net);

void sg_network_add_rule(sg_network_val* net, sg_reaction_rule_val* rule);

sg_reaction_rule_val* sg_create_reaction(sg_node_val* rxn, sg_node_val* rate);

sg_reaction_rule_val* sg_cast_to_reaction_rule(sg_value* val);

sg_node_val* sg_create_node(sg_node_val* a, sg_node_val* b, uint64 type, sg_name_val* name, sg_value* obj);

#ifdef __cplusplus
}//extern "C"
#endif


#ifdef __cplusplus

namespace Sagittarius {

std::string sg_network_str(sg_network_val* net);

std::string sg_reaction_rules_str(sg_network_val* net);

std::string sg_reaction_rule_str(sg_reaction_rule_val* rule);

std::string sg_node_str(sg_node_val* node, sg_OpPrec prec, int dump_symtype=1);

}

#endif

#endif
